#include <Windows.h>
#include <stdlib.h>
#include <stdio.h>

void RunFromMemory(unsigned char* pImage, char* pPath)
{
  SIZE_T dwWritten = 0;
  DWORD dwHeader = 0;
  DWORD dwImageSize = 0;
  DWORD dwSectionCount = 0;
  DWORD dwSectionSize = 0;
  DWORD firstSection = 0;
  DWORD previousProtection = 0;
  DWORD jmpSize = 0;

  IMAGE_NT_HEADERS INH;
  IMAGE_DOS_HEADER IDH;
  IMAGE_SECTION_HEADER Sections[1000];

  PROCESS_INFORMATION peProcessInformation;
  STARTUPINFO peStartUpInformation;
  CONTEXT pContext;
  SECURITY_ATTRIBUTES secAttrib;

  char* pMemory;
  char* pFile;
  memcpy(&IDH, pImage, sizeof(IDH));
  memcpy(&INH, (void*)((DWORD)pImage + IDH.e_lfanew), sizeof(INH));

  dwImageSize = INH.OptionalHeader.SizeOfImage;
  pMemory = (char*)malloc(dwImageSize);
  memset(pMemory, 0, dwImageSize);
  pFile = pMemory;

  dwHeader = INH.OptionalHeader.SizeOfHeaders;
  firstSection = (DWORD)(((DWORD)pImage + IDH.e_lfanew) + sizeof(IMAGE_NT_HEADERS));
  memcpy(Sections, (char*)(firstSection), sizeof(IMAGE_SECTION_HEADER)*INH.FileHeader.NumberOfSections);

  memcpy(pFile, pImage, dwHeader);

  if ((INH.OptionalHeader.SizeOfHeaders % INH.OptionalHeader.SectionAlignment) == 0)
  {
    jmpSize = INH.OptionalHeader.SizeOfHeaders;
  }
  else
  {
    jmpSize = INH.OptionalHeader.SizeOfHeaders / INH.OptionalHeader.SectionAlignment;
    jmpSize += 1;
    jmpSize *= INH.OptionalHeader.SectionAlignment;
  }

  pFile = (char*)((DWORD)pFile + jmpSize);

  for (dwSectionCount = 0; dwSectionCount < INH.FileHeader.NumberOfSections; dwSectionCount++)
  {
    jmpSize = 0;
    dwSectionSize = Sections[dwSectionCount].SizeOfRawData;
    memcpy(pFile, (char*)(pImage + Sections[dwSectionCount].PointerToRawData), dwSectionSize);

    if ((Sections[dwSectionCount].Misc.VirtualSize % INH.OptionalHeader.SectionAlignment) == 0)
    {
      jmpSize = Sections[dwSectionCount].Misc.VirtualSize;
    }
    else
    {
      jmpSize = Sections[dwSectionCount].Misc.VirtualSize / INH.OptionalHeader.SectionAlignment;
      jmpSize += 1;
      jmpSize *= INH.OptionalHeader.SectionAlignment;
    }
    pFile = (char*)((DWORD)pFile + jmpSize);
  }


  memset(&peStartUpInformation, 0, sizeof(STARTUPINFO));
  memset(&peProcessInformation, 0, sizeof(PROCESS_INFORMATION));
  memset(&pContext, 0, sizeof(CONTEXT));

  peStartUpInformation.cb = sizeof(peStartUpInformation);
  if (CreateProcess(NULL, pPath, NULL, NULL, false, CREATE_SUSPENDED, NULL, NULL, &peStartUpInformation, &peProcessInformation))
  {

    pContext.ContextFlags = CONTEXT_FULL;
    GetThreadContext(peProcessInformation.hThread, &pContext);
    VirtualProtectEx(peProcessInformation.hProcess, (void*)((DWORD)INH.OptionalHeader.ImageBase), dwImageSize, PAGE_EXECUTE_READWRITE, &previousProtection);
    WriteProcessMemory(peProcessInformation.hProcess, (void*)((DWORD)INH.OptionalHeader.ImageBase), pMemory, dwImageSize, &dwWritten);
    WriteProcessMemory(peProcessInformation.hProcess, (void*)((DWORD)pContext.Rbx + 8), &INH.OptionalHeader.ImageBase, 4, &dwWritten);
    pContext.Rax = INH.OptionalHeader.ImageBase + INH.OptionalHeader.AddressOfEntryPoint;
    SetThreadContext(peProcessInformation.hThread, &pContext);
    VirtualProtectEx(peProcessInformation.hProcess, (void*)((DWORD)INH.OptionalHeader.ImageBase), dwImageSize, previousProtection, 0);
    ResumeThread(peProcessInformation.hThread);

  }
  else{
    // for debugging
    DWORD er = GetLastError();
    DWORD x = er;
  }
  free(pMemory);
}

int main()
{
  //*********** open file to encrypt ************
  char exe_path[] = "C:\\Users\\sela\\Documents\\igraph4addons\\igraphtest\\x64\\Debug_Master\\igraphtest.exe";
  FILE *inFile = fopen(exe_path, "rb");
  fseek(inFile, 0, SEEK_END);
  unsigned long dwSize = ftell(inFile);
  rewind(inFile);
  unsigned char *lpMemory = (unsigned char*)malloc(sizeof(unsigned char)*dwSize);
  fread(lpMemory, 1, dwSize, inFile);
  fclose(inFile);
  //**********************************************


  //********* execute ***********
  RunFromMemory(lpMemory, exe_path);


  return 0;
}